#include "RecuitSim.h"
#include "Piece.h"
#include <time.h>
#include <Windows.h>

RecuitSim::RecuitSim(Plateau * plateau, int K_Depart, int K_Diminution, int K_Palier, int tps)
{
	currentPlateau = new Plateau(plateau);

	temperature=10000;

	K = K_Depart;
	Korigine = K;
	_diminution = K_Diminution;
	_palier = K_Palier;
	RotaChange = true;
	_resultDiminuant = 0;
	_cptDiminuant = 0;

	this->tps=tps;
}


RecuitSim::~RecuitSim(void)
{
}

double RecuitSim::Now()
{
	double freq, t;
	QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
	QueryPerformanceCounter((LARGE_INTEGER*)&t);
	return (((t*1000000000)/freq)/1000000000);
}


void RecuitSim::Compare()
{
	int check = currentPlateau.Check();

	if(check >= saveScore)
	{
		if(check > bestScore)
		{
			bestPlateau.ChargerPlateau(&currentPlateau);
			bestScore = check;
			cout << "New best : " << bestScore << endl;
		}
		savePlateau.ChargerPlateau(&currentPlateau);
		saveScore = check;
	}else
	{
		//int delta = saveScore - check;
		//if(exp((double)(-delta/K)) > rand()%10)
		//{
		//	savePlateau.ChargerPlateau(&currentPlateau);
		//	saveScore = check;
		//}else
		currentPlateau.ChargerPlateau(&savePlateau);
	}

	//temperature--;
}

void RecuitSim::Diminuant(int resultat)
{
	if(resultat == _resultDiminuant && K > 1)
	{
		_cptDiminuant += 1;

		if(_cptDiminuant == _palier)
		{
			K -= _diminution;
			_cptDiminuant = 0;
			RotaChange = !RotaChange;
		}
	}
	else
	{
		if(resultat == _resultDiminuant)
		{
			_cptDiminuant += 1;

			if(_cptDiminuant == (_palier))
			{
				K = Korigine;
				_cptDiminuant = 0;
			}
		}
		else
		{
			_resultDiminuant = resultat;
			_cptDiminuant = 0;
		}
	}
}

void RecuitSim::RotationAleatoir()
{
	int tmpX1, tmpY1, tmpX2, tmpY2, rota1, rota2;

	for(int i = 0 ; i<K ; i++)
	{
		tmpX1 = (rand()%14)+1;
		tmpY1 = (rand()%14)+1;
		tmpX2 = (rand()%14)+1;
		tmpY2 = (rand()%14)+1;
		rota1 = (rand()%3)+1;
		rota2 = (rand()%3)+1;
		currentPlateau.getPiece(tmpX1, tmpY1)->SetOrientation(rota1);
		currentPlateau.getPiece(tmpX2, tmpY2)->SetOrientation(rota2);
	}
}

void RecuitSim::EchangeAleatoir()
{
	Piece *tmpPiece1, *tmpPiece2;
	int tmpX1, tmpY1, tmpX2, tmpY2;

	for(int i = 0 ; i<K ; i++)
	{

		tmpX1 = rand()%16;
		tmpY1 = rand()%16;

		tmpX2 = rand()%16;
		tmpY2 = rand()%16;

		if(tmpX1 == 0 || tmpX1 == 15)
		{
			if(tmpY1 == 0 || tmpY1 == 15)
			{
				if(rand()%2 == 1)
					tmpX2 = 0;
				else
					tmpX2 = 15;
				if(rand()%2 == 1)
					tmpY2 = 0;
				else
					tmpY2 = 15;
			}else
			{
				if(rand()%2 == 1)
				{
					if(rand()%2 == 1)
					{
						tmpX2 = 15;
					}else
					{
						tmpX2 = 0;
					}
					tmpY2 = (rand()%14)+1;
				}else
				{
					if(rand()%2 == 1)
					{
						tmpY2 = 15;
					}else
					{
						tmpY2 = 0;
					}
					tmpX2 = (rand()%14)+1;
				}
			}
		}else if(tmpY1 == 0 || tmpY1 == 15)
		{
			if(rand()%2 == 1)
			{
				if(rand()%2 == 1)
				{
					tmpX2 = 15;
				}else
				{
					tmpX2 = 0;
				}
				tmpY2 = (rand()%14)+1;
			}else
			{
				if(rand()%2 == 1)
				{
					tmpY2 = 15;
				}else
				{
					tmpY2 = 0;
				}
				tmpX2 = (rand()%14)+1;
			}
		}

		tmpPiece1 = currentPlateau.getPiece(tmpX1, tmpY1);
		tmpPiece2 = currentPlateau.getPiece(tmpX2, tmpY2);

		int tmpOrient = tmpPiece2->Orientation;
		tmpPiece2->SetOrientation(tmpPiece1->Orientation);
		tmpPiece1->SetOrientation(tmpOrient);

		currentPlateau.setPiece(tmpX1, tmpY1, tmpPiece2);
		currentPlateau.setPiece(tmpX2, tmpY2, tmpPiece1);
	}
}

void RecuitSim::Run()
{
	// Compteur
	double t = Now();

	bestPlateau = new Plateau(&currentPlateau);
	savePlateau = new Plateau(&currentPlateau);
	bestScore = saveScore = currentPlateau.Check();

	while(Now() - t < tps)
	{
		if(RotaChange)
		{
			RotationAleatoir();
		}else
		{
			EchangeAleatoir();
		}

		Compare();

		Diminuant(bestScore);
	}
}

Plateau* RecuitSim::GetBestPlateau()
{
	return &bestPlateau;
}